<template>
	<div class="pro-table">
		<SearchForm
			v-if="filterColumns.length"
			ref="formRef"
			class="card form-main"
			v-bind="searchConfig"
			:form-list="filterColumns"
			:search-param="searchInfo"
			:enum-map="enumMap"
			@query="handleQuery"
			@resetQuery="handleResetQuery"
		>
			<template
				v-for="slot in Object.keys({ ...$scopedSlots, ...$slots })"
				:slot="slot"
				slot-scope="scope"
			>
				<slot :name="slot" v-bind="scope"></slot>
			</template>
		</SearchForm>

		<div class="card table-main">
			<div class="table-header">
				<div v-if="title" class="table-header-title">
					{{ title }}
				</div>
				<div class="header-button-left">
					<slot name="tableHeader" :selectedIds="selectedIds" :selectedList="selectedList"> </slot>
				</div>
				<div class="header-button-right">
					<slot name="toolButton">
						<el-popover v-if="toolbarConfig.import" placement="top" width="60" trigger="hover">
							<div style="text-align: center; margin: 0">
								<el-button size="mini" type="text" @click="templateApi">下载模板</el-button>
								<el-button size="mini" type="text" @click="$refs.importDialog.open()"
									>导入</el-button
								>
							</div>
							<i class="el-icon-upload2" @click="templateApi" slot="reference" />
						</el-popover>
						<i v-if="toolbarConfig.export" class="el-icon-download" @click="handleExport" />
						<i v-if="toolbarConfig.refresh" class="el-icon-refresh" @click="refresh" />
						<i
							v-if="toolbarConfig.setCol"
							class="el-icon-setting"
							@click="$refs.colSetRef.showDialog()"
						/>
					</slot>
				</div>
			</div>

			<el-table
				ref="tableRef"
				v-loading="loading"
				v-on="$listeners"
				v-bind="$attrs"
				:data="tableData"
				:row-key="rowKey"
				@selection-change="handleSelectionChange"
			>
				<template v-for="item in tableColumns">
					<!-- selection || index -->
					<el-table-column
						:key="item.prop ?? item.type"
						v-bind="item"
						:align="item.align ?? 'center'"
						:width="item.width ?? '55'"
						:reserve-selection="item.type == 'selection'"
						v-if="item.type == 'selection'"
					>
					</el-table-column>
					<el-table-column
						:key="item.prop ?? item.type"
						v-bind="item"
						:align="item.align ?? 'center'"
						:width="item.width ?? '55'"
						:index="item.indexMethod ?? indexMethod"
						v-if="item.type == 'index'"
					>
					</el-table-column>
					<!-- expand -->
					<el-table-column
						:key="item.prop ?? item.type"
						v-bind="item"
						:align="item.align ?? 'center'"
						:width="item.width ?? '80'"
						v-if="item.type == 'expand'"
						v-slot="scope"
					>
						<slot name="expand" v-bind="scope"></slot>
					</el-table-column>
					<el-table-column
						v-if="!item.type && item.prop && !item.hidden"
						v-bind="item"
						:align="item.align ?? 'center'"
						:key="item.prop"
						:column-key="item.prop"
						:prop="item.prop"
						:label="item.label"
						:width="item.width"
						:min-width="item.minWidth"
						:fixed="item.fixed"
						:header-align="item.headerAlign"
						:sortable="item.sortable"
						:show-overflow-tooltip="
							item.showOverflowTooltip === false || item.prop === COLUMN_OPERATE_PROP ? false : true
						"
						:filters="item.filters && item.filters"
					>
						<template slot-scope="{ row, $index }">
							<slot :name="item.prop" :row="row" :index="$index" :col="item">
								<template v-if="item.prop === COLUMN_OPERATE_PROP">
									<Button
										v-for="(btn, i) in getButtons(row, item)"
										:key="i"
										:btn="btn"
										:row="row"
										:index="$index"
										:data="tableData"
									/>
								</template>
								<span v-else>{{ renderCellData(item, row, $index) }}</span>
							</slot>
						</template>
					</el-table-column>
				</template>
				<!-- 插入表格最后一行之后的插槽 -->
				<template #append>
					<slot name="append"> </slot>
				</template>
				<!-- 表格无数据情况 -->
				<template #empty>
					<div class="table-empty">
						<slot name="empty">
							<!-- <img src="@/assets/images/notData.png" alt="notData" /> -->
							<div>暂无数据</div>
						</slot>
					</div>
				</template>
			</el-table>

			<Pagination
				v-if="pagination"
				:pagination="pagData"
				@size-change="handleSizeChange"
				@current-change="handleCurrentChange"
			/>
		</div>

		<ColumnsSetting
			v-if="toolbarConfig.setCol"
			ref="colSetRef"
			:settingColumns="settingColumns"
			@change="handleColumnsChange"
		/>

		<!-- 导出excel -->
		<ExcelImport ref="importDialog" :import-api="importApi" @success="handleImport" />
	</div>
</template>

<script>
import SearchForm from "./components/SearchForm.vue";
import Pagination from "./components/Pagination.vue";
import Button from "./components/Button.vue";
import ColumnsSetting from "./components/ColumnsSetting.vue";
import ExcelImport from "./components/ExcelImport.vue";
import { filterEnum, formatValue, getValidKey, COLUMN_OPERATE_PROP } from "./utils";

// 搜索表格
// @group Table
export default {
	name: "ProTable",
	inheritAttrs: false,
	components: {
		SearchForm,
		Pagination,
		Button,
		ColumnsSetting,
		ExcelImport,
	},
	props: {
		// 查询表单配置
		searchConfig: {
			type: Object,
			default: () => ({}),
		},
		// 工具栏配置
		toolbarConfig: {
			type: Object,
			default: () => ({
				import: false,
				export: false,
				refresh: false,
				setCol: false,
			}),
		},
		// 标题
		title: {
			type: String,
			default: "",
		},
		// 表格列
		columns: {
			type: Array,
			default: () => [],
		},
		// 查询
		rowKey: {
			type: String,
			default: "id",
		},
		// 分页器配置
		pagination: {
			type: [Object, Boolean],
			default: true,
		},
		// 额外查询参数
		params: {
			type: Object,
			default: () => ({}),
		},
		// 查询
		request: {
			type: Function,
			default: () => new Promise(resolve => resolve({})),
		},
		// 是否初始化时加载表格数据
		requestAuto: {
			type: Boolean,
			default: true,
		},
		// 导入接口
		// eslint-disable-next-line vue/require-default-prop
		importApi: {
			type: Function,
		},
		// 下载模板接口
		templateApi: {
			type: Function,
			default: () => new Promise(resolve => resolve({})),
		},
		// 导出接口
		exportApi: {
			type: Function,
			default: () => new Promise(resolve => resolve({})),
		},
	},
	data() {
		this.COLUMN_OPERATE_PROP = COLUMN_OPERATE_PROP;
		return {
			// 查询条件
			searchInfo: {},
			// 分页器
			pagData: {
				pageNum: 1,
				pageSize: 10,
				total: 0,
			},
			// 加载状态
			loading: false,
			// 条件搜索列
			filterColumns: [],
			// 可视表格列
			tableColumns: [],
			// 列表数据源
			tableData: [],
			// 选中项
			selectedList: [],
		};
	},
	computed: {
		// 异步请求的字典列表格式化单元格内容 || 填充搜索下拉选择
		enumMap() {
			return this.columns.reduce((cur, next) => {
				if (next.options && next.prop) {
					cur[next.prop] = next.options;
				}
				return cur;
			}, {});
		},
		settingColumns() {
			return this.tableColumns.filter(
				item => !["selection", "index", "expand"].includes(item.type)
			);
		},
		// 获取操作列按钮
		getButtons() {
			return (row, item) => {
				if (!item.buttons) return [];
				return item.buttons.filter(btn => {
					return typeof btn.hidden === "function" ? !btn.hidden(row) : true;
				});
			};
		},
		// 表格选中项ID集合
		selectedIds() {
			return this.selectedList.map(item => item[this.rowKey]);
		},
	},
	watch: {
		params: {
			handler() {
				this.handleRequest();
			},
			deep: true,
		},
	},
	created() {
		this.init();
	},
	methods: {
		isFunction(fn) {
			return typeof fn === "function";
		},
		init() {
			this.initFilterColumns();
			this.initColumns();
			this.initRequestParam();
			if (this.requestAuto) {
				this.handleRequest();
			}
		},
		// 初始化条件搜索列
		initFilterColumns() {
			this.filterColumns = this.columns
				.filter(o => o?.search?.valueType)
				.map((item, idx) => {
					const { label, prop, search, options } = item;
					const key = search?.key ?? prop;
					const order = search?.order ?? idx;
					const newItem = {
						label,
						...search,
						key,
						order,
					};
					return options ? { options, ...newItem } : newItem;
				})
				.sort((a, b) => a.order - b.order);
		},
		// 初始化表格列
		initColumns() {
			// hideInTable:不在表格显示
			this.tableColumns = this.columns
				.filter(item => !item.hideInTable)
				.map(item => {
					item.hidden = item.hidden ?? false;
					return item;
				});
		},
		// 设置列
		handleColumnsChange(item) {
			this.tableColumns = this.tableColumns.map(v => {
				return v.prop === item.prop ? item : v;
			});
		},
		// 初始化查询条件
		initSearchInfo() {
			this.searchInfo = this.columns.reduce((cur, next) => {
				const { prop, defaultValue } = next;
				if (defaultValue) {
					cur[prop] = defaultValue;
				}
				return cur;
			}, {});
		},
		// 更新分页器
		setPag(value) {
			this.pagData = { ...this.pagData, ...value };
		},
		// 初始化请求参数
		initRequestParam() {
			this.initSearchInfo();
			this.setPag(this.pagination);
		},
		// 获取请求参数
		getRequestParam() {
			const { pageNum, pageSize } = this.pagData;
			return {
				...getValidKey(this.searchInfo),
				pageNum,
				pageSize,
				...this.params,
			};
		},
		// 查询表格
		async handleRequest() {
			if (!this.isFunction(this.request)) return;
			this.loading = true;
			const reqParams = this.getRequestParam();
			const res = await this.request(reqParams);
			if (res) {
				const { list, total } = res;
				this.setPag({ total });
				this.tableData = list || [];
			}
			this.loading = false;
		},
		// 点击查询
		handleQuery() {
			this.$refs.formRef.validate(valid => {
				if (valid) {
					this.setPag({ pageNum: 1 });
					this.handleRequest();
				}
			});
		},
		// 重置查询条件
		handleResetQuery() {
			this.$refs.formRef.resetFields();
			this.initSearchInfo();
			this.handleRequest();
		},
		handleSizeChange(pageSize) {
			this.setPag({ pageNum: 1, pageSize });
			this.handleRequest();
		},
		handleCurrentChange(pageNum) {
			this.setPag({ pageNum });
			this.handleRequest();
		},
		// 单元格显示内容
		renderCellData(item, row, index) {
			if (typeof item.customRender === "function") {
				return item.customRender(row, index);
			}
			const cellValue = row[item.prop];

			const list = item.options || this.enumMap[item.prop];
			return list ? filterEnum(cellValue, list) : formatValue(cellValue);
		},
		// 选择事件
		handleSelectionChange(val) {
			this.selectedList = val;
		},
		indexMethod(index) {
			const { pageNum, pageSize } = this.pagData;
			return (pageNum - 1) * pageSize + index + 1;
		},
		// 导入成功,重新加载表格
		handleImport(value) {
			if (value) {
				this.reload();
			}
		},
		handleExport() {
			this.exportApi(this.selectedIds, this.getRequestParam());
		},
		// @vuese
		// 删除成功后计算页码,count:删除条数
		resetPageNum(count = 1) {
			const { pageNum, pageSize, total } = this.pagData;
			if (pageNum === 1) return 1;
			if ((pageNum - 1) * pageSize + count === total) {
				return pageNum - 1;
			}
			this.pagData.pageNum = pageNum;
		},
		// @vuese
		// 控制loading
		setLoading(flag = false) {
			this.loading = flag;
		},
		// @vuese
		// 刷新表格
		refresh() {
			this.handleRequest();
		},
		// @vuese
		// 重新加载表格,重置所有查询条件
		reload() {
			this.initRequestParam();
			this.handleRequest();
		},
		// @vuese
		// 获取el-table表格组件实例
		getTableInstance() {
			return this.$refs.tableRef;
		},
	},
};
</script>

<style lang="scss" scoped>
@import "./index.scss";
</style>
